#ifndef __WN_BYTE_SWAP_H__
#define __WN_BYTE_SWAP_H__

#include "WNCore/inc/WNPlatform.h"
#include "WNCore/inc/WNTypes.h"

#ifdef _WN_ENDIAN_BIG
//On big endian systems, we don't actually have to do any work
    template<typename T>
    void ToBigEndian(T&){
        return;
    }

    template<typename T>
    void FromBigEndian(T&){
        return;
    }
#else
    WN_INLINE void _swap(char* startPos, char* endPos){
        *startPos ^= *endPos;
        *endPos ^= *startPos;
        *startPos ^= *endPos;
    }
//On little endian systems however, this is more complicated
    template<typename T, size_t width>
    struct _swapBytes {
        void operator()(T&){
            //WN_STATIC_ASSERT_DESC(WN_FALSE, "You cannot convert this type from big endian");
        }
    };

    template<typename T>
    struct _swapBytes<T, 1> {
        void operator()(T&) {
            //WN_STATIC_ASSERT_DESC(WN_FALSE, "You should not be trying to endian swap a byte");
        }
    };

    template<typename T>
    struct _swapBytes<T, 2> {
        void operator()(T& _val) {
            _val = (_val >> 8) | ((_val & 0xFF) << 8);
        }
    };

    template<typename T>
    struct _swapBytes<T, 4> {
        void operator()(T& _val) {
            _val = ((((_val) & 0xFF000000) >> 24) | 
                   (((_val) & 0x00FF0000) >> 8 ) |
                   (((_val) & 0x0000FF00) << 8 ) |
                   (((_val) & 0x000000FF) << 24));
        }
    };

    template<typename T>
    struct _swapBytes<T, 8> {
        void operator()(T& _val) {
            _val = ((((_val) & 0xFF00000000000000ull) >> 56) | 
                    (((_val) & 0x00FF000000000000ull) >> 40) | 
                    (((_val) & 0x0000FF0000000000ull) >> 24) | 
                    (((_val) & 0x000000FF00000000ull) >> 8) | 
                    (((_val) & 0x00000000FF000000ull) << 8) | 
                    (((_val) & 0x0000000000FF0000ull) << 24) | 
                    (((_val) & 0xFF0000000000FF00ull) << 40) | 
                    (((_val) & 0xFF000000000000FFull) << 56));
        }
    };

    template<>
    struct _swapBytes<WN_FLOAT32, 4> {
        void operator()(WN_FLOAT32& _val) {
            WN_CHAR* floats = reinterpret_cast<WN_CHAR*>(&_val);
            _swap(&floats[0], &floats[3]);
            _swap(&floats[1], &floats[2]);
        }
    };

    template<>
    struct _swapBytes<WN_FLOAT64, 8> {
        void operator()(WN_FLOAT64& _val) {
            WN_CHAR* floats = reinterpret_cast<WN_CHAR*>(&_val);
            _swap(&floats[0], &floats[7]);
            _swap(&floats[1], &floats[6]);
            _swap(&floats[2], &floats[5]);
            _swap(&floats[3], &floats[4]);   
        }
    };

    template<typename T>
    void ToBigEndian(T& _val){
        _swapBytes<T, sizeof(T)>()(_val);
    }

    template<typename T>
    void FromBigEndian(T& _val){
        _swapBytes<T, sizeof(T)>()(_val);
    }
#endif

#endif//__WN_BYTE_SWAP_H__
